#pragma once

#include <fstream>
#include <string>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <sys/stat.h>
#include "Log.hpp"

namespace micromq
{
    class FileHelper
    {
    public:
        FileHelper(const std::string& fileName): _fileName(fileName)
        {}

        static bool createFile(const std::string& fileName)
        {
            if(isExists(fileName)) return true;
            std::string path = getParentPath(fileName); // /aaa/bbb/ccc
            if(!isPathExists(path)) createDir(path);

            std::ofstream ofs(fileName, std::ios::binary|std::ios::out);
            if(ofs.is_open() == false)
            {
                logError("%s open error...", fileName.c_str());
                return false;
            }
            ofs.close();
            return true;
        }

        static bool removeFile(const std::string& fileName)
        {
            return ::remove(fileName.c_str()) == 0;
        }

        static bool createDir(const std::string& path)
        {
            if(isPathExists(path)) return true;
            int pos = 0, index = 0;
            while(pos < path.size() && index < path.size())
            {
                pos = path.find("/", index);
                if(pos == std::string::npos)
                {
                    return ::mkdir(path.c_str(), 0775) == 0; //-rwxrwxr-x
                }
                std::string subPath = path.substr(0, pos);
                if(::mkdir(subPath.c_str(), 0775) != 0 && errno != EEXIST)
                {
                    logError("mkdir %s error...", path.c_str());
                    return false;
                }
                index = pos + 1;
            }
            return true;
        }

        static bool removeDir(const std::string& path)
        {
            std::string cmd = "rm -rf " + path;
            return system(cmd.c_str()) != -1;
        }

        static std::string getParentPath(const std::string fileName)
        {
            // /aaa/bbb/ccc
            int pos = fileName.find_last_of("/");
            if(pos == std::string::npos) return "./";
            return fileName.substr(0, pos);
        }

        static bool rename(const std::string& oldName, const std::string& newName)
        {
            return ::rename(oldName.c_str(), newName.c_str()) == 0;
        }

        static bool read(const std::string& fileName, char* buffer, int pos, int len)
        {
            std::ifstream ifs(fileName, std::ios::binary|std::ios::in); //打开文件
            if(ifs.is_open() == false)
            {
                logError("%s open error...", fileName.c_str());
                return false;
            }
            
            ifs.seekg(pos, std::ios::beg); //跳转至指定位置
            ifs.read(buffer, len);
            if(ifs.good() == false)
            {
                logError("%s read error...", fileName.c_str());
                ifs.close();
                return false;
            }
            buffer[len] = '\0';
            ifs.close();
            return true;
        }

        static bool readAll(const std::string& fileName, char* buffer)
        {
            return read(fileName, buffer, 0, size(fileName));
        }

        static bool write(const std::string& fileName, const char* buffer, int pos, int len)
        {
            std::fstream fs(fileName, std::ios::binary|std::ios::in|std::ios::out); //打开文件，读权限用于位置跳转
            if(fs.is_open() == false)
            {
                logError("%s open error...", fileName.c_str());
                return false;
            }
            fs.seekp(pos, std::ios::beg);
            fs.write(buffer, len);
            if(fs.good() == false) 
            {
                logError("%s write error...");
                fs.close();
                return false;
            }
            fs.close();
            return true;
        }

        static bool append(const std::string& fileName, const char* buffer, int len)
        {
            return write(fileName, buffer, size(fileName), len);
        }

        static bool isExists(const std::string& fileName)
        {
            struct stat st;
            return stat(fileName.c_str(), &st) == 0;
        }

        static bool isPathExists(const std::string& path)
        {
            struct stat st;
            return stat(path.c_str(), &st) == 0;
        }

        static int size(const std::string& fileName)
        {
            struct stat st;
            int ret = stat(fileName.c_str(), &st);
            if(ret < 0) return -1;
            return st.st_size;
        }

        std::string getFileName()
        {
            return _fileName;
        }

        ~FileHelper()
        {}
    private:
        std::string _fileName;
    };
}